<template>
  <div>
    <el-dropdown
      v-if="hasMenu"
      ref="ref-el-dropdown"
      trigger="click"
      @command="menuCommand"
    >
      <span />
      <el-dropdown-menu
        slot="dropdown"
        :visible-arrow="false"
      >
        <el-dropdown-item
          v-for="(item, i) in menu.items"
          :key="`mi_${i}`"
          v-bind="item.props"
        >
          <span>{{ item.text }}</span>
        </el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
    <el-tree
      ref="ref-el-tree"
      v-loading="loading"
      v-bind="props"
      v-on="events"
    />
  </div>
</template>

<script lang="ts">
/*
prop 列表
  CM: ComponentModel  组件模型
  linkage: boolean    组件默认联动状态 [默认为 false]
CM.meta 扩展
  menu: ComponentModel  [参考 Dropdown]
ref 列表
  ref-el-tree [默认]
  ref-el-dropdown

注意：
  01. 此组件实现使用了部分 hack 方式，部分功能标准 API 并未提供，在版本更新后可能失效
*/
import { Component, Prop, Vue } from 'vue-property-decorator'
import { Tree } from 'element-ui'
import TgComponent from '../TgComponent'
import { ComponentModel } from '@TG/interface'
import { isEmpty, isNotEmpty } from '@TG/kit/validate'
import { appModule } from '@TG/store/modules/app'

@Component({
  name: 'ElTreePackage'
})
export default class extends TgComponent {
  @Prop({ type: Object, required: true }) private CM!: ComponentModel
  @Prop({ type: Boolean, default: false }) private loading!: boolean

  private data: any = {}

  get props() {
    /*
    el-tree 的 props 似乎有个很奇怪的特性
    更新 props 似乎不会生效，el-tree 依然使用之前的 props
    举例：
      页面初始化时未指定 props.props.label 既使用默认配置，取 data.label
      后续更新 props.props.label = function，刷新（doCreateChildren）节点，节点 label 不显示，猜测依然使用了初始化时的配置
    */
    return Object.assign(
      {
        'highlight-current': true,
        'expand-on-click-node': false,
        'show-checkbox': true
      },
      this.CM.props,
      this.$attrs
    )
  }

  get events() {
    return Object.assign({ 'node-contextmenu': this.nodeContextmenu }, this.CM.events, this.$listeners)
  }

  get hasMenu() {
    return isNotEmpty(this.CM.meta?.menu?.items)
  }

  get menu() {
    return this.CM.meta?.menu
  }

  private nodeContextmenu(event: any, data: any, node: any, vm: Vue) {
    if (this.hasMenu) {
      this.data = data
      // dropdownElm、show、hide 为查看源代码后确认的属性、方法，Dropdown 接口中未暴露此属性，因此 ref 断言为 any
      const ref = this.ref('el-dropdown') as any
      const dropdownElm = ref.dropdownElm
      ref.hide()
      /*
      由于 show、hide 方法未提供回调函数，因此只能通过 setTimeout 进行处理
      show、hide 方法都属于 JS 内部计算，延迟较低，setTimeout 有一定可靠性
      */
      setTimeout(() => {
        ref.show()
        setTimeout(() => {
          const eventX: number = event.x
          const eventY: number = event.y
          const menuWidth: number = dropdownElm.offsetWidth
          const menuHeight: number = dropdownElm.offsetHeight
          const menuMargin: number = 12 + 5 // top + bottom
          let top: number = 0
          let left: number = 0
          if (menuHeight + menuMargin + eventY >= appModule.pageSize.height) {
            top = eventY - menuHeight - menuMargin
          } else {
            top = eventY
          }
          if (menuWidth + eventX >= appModule.pageSize.width) {
            left = eventX - menuWidth
          } else {
            left = eventX
          }
          const style = dropdownElm.style
          style.top = `${top}px`
          style.left = `${left}px`
        }, 25)
      }, 100)
    }
  }

  private menuCommand(command: any) {
    const call = this.menu?.events?.command
    if (isNotEmpty(call)) {
      call(command, this.data)
    }
  }

  public filterNode(queryString: any) {
    const ref = this.ref() as Tree
    if (isNotEmpty(ref)) {
      ref.filter(queryString)
    }
  }

  public reloadNode(parent?: any) {
    /*
    刷新节点只需要处理 lazy 模式
    data 模式对节点的修改是对数据的直接修改，数据的变化会直接导致树的重新渲染
    */
    const { lazy, load } = this.props
    if (isNotEmpty(lazy) && isNotEmpty(load)) {
      // root 为查看源代码后确认的属性，Tree 接口中未暴露此属性，因此 ref 断言为 any
      const ref = this.ref() as any
      if (isNotEmpty(ref)) {
        let node = parent
        if (isEmpty(node)) {
          // parent 为空则刷新整棵树
          node = ref.root
        }
        node.childNodes = []
        load(node, function(children: any) {
          // doCreateChildren 为查看源代码后确认的方法，TreeNode 接口中未暴露此方法，因此 node 断言为 any
          node.doCreateChildren(children)
        })
        ref.setCurrentKey(null)
      }
    }
  }

  public defaultRef() {
    return 'el-tree'
  }
}
</script>

<style lang="scss" scoped>
::v-deep {
  .el-dropdown {
    display: none;
  }
}
</style>
